/*LICENSE_BEGIN
 * ******************************************************************
 * All original parts of SoccerScope2003 have copyright (2003) by
 * Team YowAI, represented by Mr. Shuhei Shiota
 * The Department of Creative Informatics
 * The Graduate School of Information Science and Technology
 * The University of Tokyo
 * 
 * Additional classes (except libraries in /lib) were developed by 
 * the Dainamite Robocup-Team of the DAI-Labor, TU-Berlin 
 * (www.dai-labor.de)
 * 
 * Extended/new classes/packages are:
 *   soccerscope.analyzer.LogfileAnalyzer
 *   soccerscope.daistatistic.*
 *   soccerscope.daistatistic.charts.*
 *   soccerscope.daistatistic.tools.*
 *   soccerscope.db.*
 *   soccerscope.dialog.*
 *   soccerscope.model.PlayerWorldModel
 *   soccerscope.model.SimplePlayer
 *   soccerscope.net.AgentConnector
 *   soccerscope.training.*
 *   soccerscope.training.conditions.*
 * ******************************************************************
LICENSE_END*/
package soccerscope.dialog;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;

import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSlider;
import javax.swing.JTabbedPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import robocup.component.geometry.Vektor;
import soccerscope.model.Player;
import soccerscope.model.SoccerObject;
import soccerscope.training.ObjectTrainingModel;
import soccerscope.training.PlayerTrainingModel;
import soccerscope.training.ObjectTrainingModel.POSITION_TYPE;
import soccerscope.util.geom.Point2f;
import soccerscope.view.ObjectTrainingButton;

public class ObjectTrainingPanel extends JPanel implements ActionListener,
		MouseListener, MouseMotionListener, ChangeListener, MouseWheelListener {

	private static final long serialVersionUID = -4677894813944844081L;

	private ObjectTrainingButton objectTrainingButton;

	private JPanel speedDir;

	private JPanel other;

	private JCheckBox active;

	private ButtonGroup spdLengthGroup;

	private JSlider maxSpeedSlide;

	private JRadioButton maxSpeed;

	private JSlider minSpeedSlide;

	private JRadioButton minSpeed;

	private JSlider sdirDiffSlide;

	private JLabel slabel;

	private JSlider diffSlide;

	private JLabel alabel;

	private JLabel hlabel;

	private JLabel llabel;

	private JRadioButton rectPosition;

	private ButtonGroup positionButtons;

	private JSlider heightSlide;

	private JSlider lengthSlide;

	private JRadioButton circPosition;

	private JRadioButton fixedPosition;

	private JPanel speedLen;

	private JPanel angle;

	private JPanel position;

	private JCheckBox roundView;

	private JTabbedPane EditTabPane;

	// object: player or ball + corresp. model
	private SoccerObject object = null;

	private ObjectTrainingModel otm = null;

	// factors for scaling
	private float posFactor = 0.5f;

	private float mapFactor = 0.2f;

	private JCheckBox alignWithBody;

	/**
	 * This dialog is called when a players training setup should be defined.
	 * Therefore, the Player as well as the soccerscope instance should be given
	 * as parameter. The Player contain a PlayerTrainingModel, which will be
	 * altered using this dialog!
	 * 
	 * @param frame -
	 *            the soccerscope-instance as main-frame
	 * @param player -
	 *            the corresponding player
	 */
	public ObjectTrainingPanel() {
		super();
		initGUI();
	}

	/**
	 * This method sets the SoccerObject, which is analyzed next. It may be
	 * either a ball or a player. Depending on that, the gui shows different
	 * types of adjustment possibilities.
	 * 
	 * @param so -
	 *            the soccerobject
	 */
	public void setObject(SoccerObject so, ObjectTrainingModel otm) {
		this.object = so;
		this.otm = otm;
		this.objectTrainingButton.setObject(so);
		// init the buttons and sliders:
		this.fixedPosition.setSelected(otm.getType() == POSITION_TYPE.FIXED);
		this.rectPosition
				.setSelected(otm.getType() == POSITION_TYPE.RECTANGULAR);
		this.circPosition.setSelected(otm.getType() == POSITION_TYPE.CIRCULAR);
		this.lengthSlide.setEnabled(otm.getType() != POSITION_TYPE.FIXED);
		this.lengthSlide.setValue((int) (otm.getY_pos_diff() / this.posFactor));
		this.heightSlide.setEnabled(otm.getType() != POSITION_TYPE.FIXED);
		this.heightSlide.setValue((int) (otm.getX_pos_diff() / this.posFactor));
		if (otm instanceof PlayerTrainingModel) {
			PlayerTrainingModel ptm = (PlayerTrainingModel) otm;
			this.diffSlide.setValue((int) ptm.getBody_angle_diff());
			this.alignWithBody.setSelected(ptm.getAlignSpeedWithBody());
			this.active.setSelected(ptm.isActive());
			this.roundView.setSelected(ptm.isRoundView());

			// reorder panels
			this.EditTabPane.removeAll();
			this.EditTabPane.addTab("Position", null, this.position, null);
			this.EditTabPane.addTab("BodyAngle", null, this.angle, null);
			this.EditTabPane.addTab("Speed-Direction", null, this.speedDir,
					null);
			this.EditTabPane.addTab("Speed-Length", null, this.speedLen, null);
			this.EditTabPane.addTab("Other", null, this.other, null);

		} else {
			this.EditTabPane.remove(this.angle);
			this.EditTabPane.remove(this.other);
			// angle.setEnabled(false);
			// other.setEnabled(false);
			// diffSlide.setValue((int)ptm.getBody_angle_diff());
			// alignWithBody.setSelected(ptm.getAlignSpeedWithBody());
			// active.setSelected(ptm.isActive());
			// roundView.setSelected(ptm.isRoundView());
		}
		this.sdirDiffSlide.setValue((int) otm.getSpeed_angle_diff());
		this.minSpeedSlide.setValue(otm.getMin_speed());
		this.maxSpeedSlide.setValue(otm.getMax_speed());
		this.objectTrainingButton.repaint();
	}

	private void initGUI() {
		try {
			{
				BorderLayout thisLayout = new BorderLayout();
				this.setLayout(thisLayout);
				{
					// buttongroups
					this.positionButtons = new ButtonGroup();
					this.spdLengthGroup = new ButtonGroup();
				}
				this.objectTrainingButton = new ObjectTrainingButton();
				this.add(this.objectTrainingButton, BorderLayout.NORTH);
				this.objectTrainingButton.addMouseListener(this);
				this.objectTrainingButton.addMouseWheelListener(this);
				this.objectTrainingButton.addMouseMotionListener(this);
			}
			{
				this.EditTabPane = new JTabbedPane();
				this.add(this.EditTabPane, BorderLayout.SOUTH);
				{
					this.position = new JPanel();
					GridLayout positionLayout = new GridLayout(7, 1);
					positionLayout.setRows(7);
					this.position.setLayout(positionLayout);
					this.EditTabPane.addTab("Position", null, this.position,
							null);
					{
						this.fixedPosition = new JRadioButton();
						this.position.add(this.fixedPosition);
						this.fixedPosition.setText("FIXED");
						this.positionButtons.add(this.fixedPosition);
						this.fixedPosition.addActionListener(this);
					}
					{
						this.rectPosition = new JRadioButton();
						this.position.add(this.rectPosition);
						this.rectPosition.setText("RECTANGLE");
						this.positionButtons.add(this.rectPosition);
						this.rectPosition.addActionListener(this);
					}
					{
						this.circPosition = new JRadioButton();
						this.position.add(this.circPosition);
						this.circPosition.setText("CIRCLE");
						this.positionButtons.add(this.circPosition);
						this.circPosition.addActionListener(this);
					}
					{
						this.llabel = new JLabel();
						this.position.add(this.llabel);
						this.llabel.setText("Length-Adjust:");
					}
					{
						this.lengthSlide = new JSlider();
						this.position.add(this.lengthSlide);
						this.lengthSlide.setValue(0);
						this.lengthSlide.setPaintTicks(true);
						this.lengthSlide.setMajorTickSpacing(10);
						this.lengthSlide.addChangeListener(this);
					}
					{
						this.hlabel = new JLabel();
						this.position.add(this.hlabel);
						this.hlabel.setText("Height-Adjust:");
					}
					{
						this.heightSlide = new JSlider();
						this.position.add(this.heightSlide);
						this.heightSlide.setValue(0);
						this.heightSlide.setMajorTickSpacing(10);
						this.heightSlide.setPaintTicks(true);
						this.heightSlide.addChangeListener(this);
					}
				}
				{
					this.angle = new JPanel();
					// FlowLayout angleLayout = new FlowLayout();
					// angleLayout.setAlignment(FlowLayout.LEFT);
					// angle.setLayout(angleLayout);
					GridLayout angleLayout = new GridLayout(7, 1);
					angleLayout.setRows(7);
					this.angle.setLayout(angleLayout);
					this.EditTabPane
							.addTab("BodyAngle", null, this.angle, null);
					{
						this.alabel = new JLabel();
						this.angle.add(this.alabel);
						this.alabel.setText("Adjust Diff:");
					}
					{
						this.diffSlide = new JSlider();
						this.angle.add(this.diffSlide);
						this.diffSlide.setValue(0);
						this.diffSlide.setMaximum(180);
						this.diffSlide.setPaintTicks(true);
						this.diffSlide.setPaintLabels(true);
						this.diffSlide.setMajorTickSpacing(30);
						this.diffSlide.addChangeListener(this);
					}
				}
				{
					this.speedDir = new JPanel();
					GridLayout spdirLayout = new GridLayout(7, 1);
					spdirLayout.setRows(7);
					this.speedDir.setLayout(spdirLayout);
					// FlowLayout speedDirLayout = new FlowLayout();
					// speedDirLayout.setAlignment(FlowLayout.LEFT);
					// speedDir.setLayout(speedDirLayout);
					this.EditTabPane.addTab("Speed-Direction", null,
							this.speedDir, null);
					{
						this.alignWithBody = new JCheckBox();
						this.speedDir.add(this.alignWithBody);
						this.alignWithBody
								.setText("Align Speed-Dir with Body-Dir");
						this.alignWithBody.addActionListener(this);
					}
					{
						this.slabel = new JLabel();
						this.speedDir.add(this.slabel);
						this.slabel.setText("Speed-Dir Diffusion:");
					}
					{
						this.sdirDiffSlide = new JSlider();
						this.speedDir.add(this.sdirDiffSlide);
						this.sdirDiffSlide.setMajorTickSpacing(30);
						this.sdirDiffSlide.setPaintTicks(true);
						this.sdirDiffSlide.setPaintLabels(true);
						this.sdirDiffSlide.setMaximum(180);
						this.sdirDiffSlide.addChangeListener(this);
					}
				}
				{
					this.speedLen = new JPanel();
					GridLayout splenLayout = new GridLayout(7, 1);
					splenLayout.setRows(7);
					this.speedLen.setLayout(splenLayout);
					// FlowLayout speedLenLayout = new FlowLayout();
					// speedLenLayout.setAlignment(FlowLayout.LEFT);
					// speedLen.setLayout(speedLenLayout);
					this.EditTabPane.addTab("Speed-Length", null,
							this.speedLen, null);
					this.speedLen.setPreferredSize(new Dimension(300, 300));
					{
						this.minSpeed = new JRadioButton();
						this.speedLen.add(this.minSpeed);
						this.minSpeed.setText("Min-Speed:");
						this.minSpeed.setSelected(true);
						this.spdLengthGroup.add(this.minSpeed);
						this.minSpeed.addActionListener(this);
					}
					{
						this.minSpeedSlide = new JSlider();
						this.speedLen.add(this.minSpeedSlide);
						this.minSpeedSlide.setMajorTickSpacing(25);
						this.minSpeedSlide.setPaintTicks(true);
						this.minSpeedSlide.setPaintLabels(true);
						this.minSpeedSlide.addChangeListener(this);
					}
					{
						this.maxSpeed = new JRadioButton();
						this.speedLen.add(this.maxSpeed);
						this.maxSpeed.setText("Max-Speed");
						this.spdLengthGroup.add(this.maxSpeed);
						this.maxSpeed.addActionListener(this);
					}
					{
						this.maxSpeedSlide = new JSlider();
						this.speedLen.add(this.maxSpeedSlide);
						this.maxSpeedSlide.setMajorTickSpacing(25);
						this.maxSpeedSlide.setPaintTicks(true);
						this.maxSpeedSlide.setPaintLabels(true);
						this.maxSpeedSlide.setEnabled(false);
						this.maxSpeedSlide.addChangeListener(this);
					}
				}
				{
					this.other = new JPanel();
					GridLayout otherLayout = new GridLayout(7, 1);
					otherLayout.setRows(7);
					this.other.setLayout(otherLayout);
					// FlowLayout otherLayout = new FlowLayout();
					// otherLayout.setAlignment(FlowLayout.LEFT);
					// other.setLayout(otherLayout);
					this.EditTabPane.addTab("Other", null, this.other, null);
					{
						this.active = new JCheckBox();
						this.other.add(this.active);
						this.active.setText("Active Player");
						this.active.addActionListener(this);
					}
					{
						this.roundView = new JCheckBox();
						this.other.add(this.roundView);
						this.roundView.setText("Round-View");
						this.roundView.addActionListener(this);
					}
				}
			}
			this.setSize(420, 300);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * This method handles all actions available for for this dialog, i.e. all
	 * buttons, checkboxes and slides are handled in here!
	 */
	public void actionPerformed(ActionEvent arg0) {
		if (arg0.getSource() == this.fixedPosition) {
			this.otm.setType(POSITION_TYPE.FIXED);
			this.lengthSlide.setEnabled(false);
			this.heightSlide.setEnabled(false);
		} else if (arg0.getSource() == this.rectPosition) {
			this.otm.setType(POSITION_TYPE.RECTANGULAR);
			this.lengthSlide.setEnabled(true);
			this.heightSlide.setEnabled(true);
		} else if (arg0.getSource() == this.circPosition) {
			this.otm.setType(POSITION_TYPE.CIRCULAR);
			this.lengthSlide.setEnabled(true);
			this.heightSlide.setEnabled(true);
		} else if (arg0.getSource() == this.minSpeed) {
			this.minSpeedSlide.setEnabled(true);
			this.maxSpeedSlide.setEnabled(false);
		} else if (arg0.getSource() == this.maxSpeed) {
			this.minSpeedSlide.setEnabled(false);
			this.maxSpeedSlide.setEnabled(true);
		} else if (arg0.getSource() == this.alignWithBody) {
			if (this.object instanceof Player) {
				PlayerTrainingModel ptm = (PlayerTrainingModel) this.otm;
				ptm.setAlignSpeedWithBody(this.alignWithBody.isSelected());
			}
		} else if (arg0.getSource() == this.active) {
			if (this.object instanceof Player) {
				PlayerTrainingModel ptm = (PlayerTrainingModel) this.otm;
				ptm.setActive(this.active.isSelected());
			}
		} else if (arg0.getSource() == this.roundView) {
			if (this.object instanceof Player) {
				PlayerTrainingModel ptm = (PlayerTrainingModel) this.otm;
				ptm.setRoundView(this.roundView.isSelected());
			}
		}
	}

	/**
	 * MouseListener is only for the PlayerButton. Received MouseEvents are
	 * forwarded to these method to modify the TrainingModel in a intuitive
	 * manner! The Event depends on the active tab!
	 * 
	 * @param evt -
	 *            the mouse-event
	 */
	public void mouseClicked(MouseEvent evt) {
		// if the position-panel is active/visible
		if (this.EditTabPane.getSelectedComponent() == this.position) {
			Dimension dim = this.objectTrainingButton.getSize();
			int cy = dim.width / 2;
			int cx = dim.height / 2;
			Vektor veTo = new Vektor(Vektor.XY, evt.getPoint().x - cy, evt
					.getPoint().y
					- cx);
			veTo.mult(this.mapFactor);

			this.object.pos.x += veTo.x;
			this.object.pos.y += veTo.y;
			this.otm.setPosition(this.object.pos);
			this.objectTrainingButton.repaint();
		}
		if (this.EditTabPane.getSelectedComponent() == this.angle) {
			if (this.object instanceof Player) {
				Player player = (Player) this.object;
				PlayerTrainingModel ptm = (PlayerTrainingModel) this.otm;

				Dimension dim = this.objectTrainingButton.getSize();
				int cx = dim.width / 2;
				int cy = dim.height / 2;
				Vektor veTo = new Vektor(Vektor.XY, evt.getPoint().y - cy, evt
						.getPoint().x
						- cx);
				float angle = (float) veTo.getAngle();
				player.angle = angle;
				ptm.setBody_angle(angle);
				this.objectTrainingButton.repaint();
			}
		}
		if (this.EditTabPane.getSelectedComponent() == this.speedDir) {
			Dimension dim = this.objectTrainingButton.getSize();
			int cx = dim.width / 2;
			int cy = dim.height / 2;
			Vektor veTo = new Vektor(Vektor.XY, evt.getPoint().y - cy, evt
					.getPoint().x
					- cx);
			float angle = (float) veTo.getAngle();
			this.otm.setSpeed_angle(angle);
		}
	}

	public void mousePressed(MouseEvent evt) {
	}

	public void mouseReleased(MouseEvent evt) {
	}

	/**
	 * This method sets a point to the field, which is equivalent to the
	 * mouse-position on the trainings- button. This might help to keep
	 * orientation when the buttons positioning or turning abilities are used.
	 */
	public void mouseEntered(MouseEvent evt) {
		Dimension dim = this.objectTrainingButton.getSize();
		int cy = dim.width / 2;
		int cx = dim.height / 2;
		Vektor veTo = new Vektor(Vektor.XY, evt.getPoint().x - cy, evt
				.getPoint().y
				- cx);
		veTo.mult(this.mapFactor);

		Point2f observer = new Point2f();
		observer.x = (float) (this.object.pos.x + veTo.x);
		observer.y = (float) (this.object.pos.y + veTo.y);

		this.otm.setObservePoint(observer);
	}

	/**
	 * This method removes a point to the field, which is equivalent to the
	 * mouse-position on the trainings- button.
	 */
	public void mouseExited(MouseEvent evt) {
		this.otm.setObservePoint(null);
	}

	public void mouseDragged(MouseEvent evt) {
	}

	/**
	 * This method updates the observer-button
	 */
	public void mouseMoved(MouseEvent evt) {
		Dimension dim = this.objectTrainingButton.getSize();
		int cy = dim.width / 2;
		int cx = dim.height / 2;
		Vektor veTo = new Vektor(Vektor.XY, evt.getPoint().x - cy, evt
				.getPoint().y
				- cx);
		veTo.mult(this.mapFactor);

		Point2f observer = new Point2f();
		observer.x = (float) (this.object.pos.x + veTo.x);
		observer.y = (float) (this.object.pos.y + veTo.y);

		this.otm.setObservePoint(observer);
	}

	/**
	 * This method checks all changes in the slides of the dialog, e.g. for
	 * changes in the positioning.
	 * 
	 * @param arg0 -
	 *            the event cause by the action
	 */
	public void stateChanged(ChangeEvent arg0) {
		if (arg0.getSource() == this.lengthSlide) {
			int value = this.lengthSlide.getValue();
			this.otm.setY_pos_diff(value * this.posFactor);
		} else if (arg0.getSource() == this.heightSlide) {
			int value = this.heightSlide.getValue();
			this.otm.setX_pos_diff(value * this.posFactor);
		} else if (arg0.getSource() == this.diffSlide) {
			if (this.object instanceof Player) {
				PlayerTrainingModel ptm = (PlayerTrainingModel) this.otm;
				int value = this.diffSlide.getValue();
				ptm.setBody_angle_diff(value);
			}
		} else if (arg0.getSource() == this.sdirDiffSlide) {
			int value = this.sdirDiffSlide.getValue();
			this.otm.setSpeed_angle_diff(value);
		} else if (arg0.getSource() == this.minSpeedSlide) {
			int value = this.minSpeedSlide.getValue();
			this.otm.setMin_speed(value);
			this.maxSpeedSlide.setValue(this.otm.getMax_speed());
		} else if (arg0.getSource() == this.maxSpeedSlide) {
			int value = this.maxSpeedSlide.getValue();
			this.otm.setMax_speed(value);
			this.minSpeedSlide.setValue(this.otm.getMin_speed());
		}
	}

	/**
	 * This method is used to turn the body/speed-dirs angle in small steps -
	 * moving the wheel up will increase the angle, moving down decreases.
	 * 
	 * @param evt -
	 *            the mouse-wheel event
	 */
	public void mouseWheelMoved(MouseWheelEvent evt) {
		// change the body angle
		if (this.EditTabPane.getSelectedComponent() == this.angle) {
			if (this.object instanceof Player) {
				Player player = (Player) this.object;
				PlayerTrainingModel ptm = (PlayerTrainingModel) this.otm;
				if (evt.getWheelRotation() < 0) {
					player.angle += 2;
					ptm.setBody_angle(player.angle);
				} else {
					player.angle -= 2;
					ptm.setBody_angle(player.angle);
				}
				this.objectTrainingButton.repaint();
			}
		} else if (this.EditTabPane.getSelectedComponent() == this.speedDir) {
			if (evt.getWheelRotation() < 0) {
				this.otm.setSpeed_angle(this.otm.getSpeed_angle() + 2);
			} else {
				this.otm.setSpeed_angle(this.otm.getSpeed_angle() - 2);
			}
		}
	}

}
